package com.yc.common.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 数学表达式生成器工具类
 * 用于生成验证码的数学表达式，确保结果在指定范围内且只有整数
 */
public class MathExpressionGenerator {

    // 运算符: +, -, ×, ÷
    private static final char[] OPERATORS = {'+', '-', '×', '÷'};

    // 运算符类型
    private static final int PLUS = 0;
    private static final int MINUS = 1;
    private static final int MULTIPLY = 2;
    private static final int DIVIDE = 3;

    // 默认结果范围
    private static final int DEFAULT_MIN_RESULT = 20;
    private static final int DEFAULT_MAX_RESULT = 120;

    // 随机数生成器
    private final Random random;

    // 结果范围
    private final int minResult;
    private final int maxResult;

    /**
     * 使用默认结果范围构造生成器
     */
    public MathExpressionGenerator() {
        this(DEFAULT_MIN_RESULT, DEFAULT_MAX_RESULT);
    }

    /**
     * 使用指定的结果范围构造生成器
     *
     * @param minResult 最小结果值
     * @param maxResult 最大结果值
     */
    public MathExpressionGenerator(int minResult, int maxResult) {
        this.random = new Random();
        this.minResult = minResult;
        this.maxResult = maxResult;
    }

    /**
     * 生成数学表达式
     *
     * @return 含有表达式和结果的Result对象
     */
    public Result generate() {
        // 随机决定使用一个或两个运算符
        boolean useTwoOperators = random.nextBoolean();

        if (useTwoOperators) {
            return generateTwoOperatorExpression();
        } else {
            return generateSingleOperatorExpression();
        }
    }

    /**
     * 生成只有一个运算符的表达式
     */
    private Result generateSingleOperatorExpression() {
        int operatorType;
        int operand1;
        int operand2;
        int result;

        // 尝试最多10次生成符合条件的表达式
        for (int attempt = 0; attempt < 10; attempt++) {
            // 随机选择运算符
            operatorType = random.nextInt(4);

            switch (operatorType) {
                case PLUS: // 加法: a + b = result
                    result = random.nextInt(maxResult - minResult + 1) + minResult;
                    operand1 = random.nextInt(result - 1) + 1; // 确保operand1 < result
                    operand2 = result - operand1;
                    break;

                case MINUS: // 减法: a - b = result
                    result = random.nextInt(maxResult - minResult + 1) + minResult;
                    operand1 = result + random.nextInt(30) + 5; // a = result + 随机值(5-34)
                    operand2 = operand1 - result;
                    break;

                case MULTIPLY: // 乘法: a × b = result
                    // 找到result的因数
                    result = random.nextInt(maxResult - minResult + 1) + minResult;
                    List<Integer> factors = findFactors(result);

                    if (factors.isEmpty()) {
                        // 如果找不到因数，尝试另一次迭代
                        continue;
                    }

                    // 随机选择一个因数
                    operand1 = factors.get(random.nextInt(factors.size()));
                    operand2 = result / operand1;
                    break;

                case DIVIDE: // 除法: a ÷ b = result
                    result = random.nextInt(maxResult - minResult + 1) + minResult;
                    operand2 = random.nextInt(8) + 2; // 除数范围: 2-9
                    operand1 = result * operand2; // 确保能够整除
                    break;

                default:
                    // 默认使用加法
                    result = random.nextInt(maxResult - minResult + 1) + minResult;
                    operand1 = random.nextInt(result - 1) + 1;
                    operand2 = result - operand1;
            }

            // 构建表达式
            String expression = operand1 + " " + OPERATORS[operatorType] + " " + operand2;

            return new Result(expression, result);
        }

        // 如果多次尝试后仍无法生成合适的表达式，返回简单加法
        return generateSimplePlus();
    }

    /**
     * 生成有两个运算符的表达式
     */
    private Result generateTwoOperatorExpression() {
        int[] operators = new int[2];
        int[] operands = new int[3];
        int result;

        // 尝试最多15次生成符合条件的表达式
        for (int attempt = 0; attempt < 15; attempt++) {
            // 随机选择两个运算符
            operators[0] = random.nextInt(4);
            operators[1] = random.nextInt(4);

            // 检查是否有除法运算
            boolean hasDivision = operators[0] == DIVIDE || operators[1] == DIVIDE;

            // 如果有除法，我们需要特殊处理确保整数结果
            if (hasDivision) {
                if (!generateDivisionExpressionOperands(operators, operands)) {
                    continue; // 如果无法生成合适的操作数，尝试下一次
                }
            } else {
                // 如果没有除法，正常生成操作数
                generateNormalOperands(operators, operands);
            }

            // 计算结果
            result = calculateResult(operators, operands);

            // 检查结果是否在范围内
            if (result >= minResult && result <= maxResult) {
                // 构建表达式
                String expression = operands[0] + " " + OPERATORS[operators[0]] + " " +
                        operands[1] + " " + OPERATORS[operators[1]] + " " + operands[2];

                return new Result(expression, result);
            }
        }

        // 如果多次尝试后仍无法生成合适的表达式，返回简单加法
        return generateSimplePlus();
    }

    /**
     * 生成包含除法的表达式的操作数
     */
    private boolean generateDivisionExpressionOperands(int[] operators, int[] operands) {
        // 确保第一个操作数有一个合理的值
        operands[0] = random.nextInt(40) + 10; // 范围: 10-49

        // 检查第一个运算符是否为除法
        if (operators[0] == DIVIDE) {
            // 找到能整除第一个操作数的除数
            List<Integer> divisors = findDivisors(operands[0]);
            if (divisors.isEmpty()) {
                return false;
            }

            // 选择一个除数
            operands[1] = divisors.get(random.nextInt(divisors.size()));

            // 生成第三个操作数
            if (operators[1] == PLUS || operators[1] == MINUS) {
                operands[2] = random.nextInt(20) + 5; // 范围: 5-24
            } else if (operators[1] == MULTIPLY) {
                operands[2] = random.nextInt(8) + 2; // 范围: 2-9
            } else { // 第二个运算符也是除法
                // 计算第一个除法的结果
                int firstDivisionResult = operands[0] / operands[1];

                // 找到能整除这个结果的除数
                List<Integer> secondDivisors = findDivisors(firstDivisionResult);
                if (secondDivisors.isEmpty()) {
                    return false;
                }

                operands[2] = secondDivisors.get(random.nextInt(secondDivisors.size()));
            }
        } else {
            // 第二个运算符是除法

            // 生成第二个操作数
            if (operators[0] == PLUS || operators[0] == MINUS) {
                operands[1] = random.nextInt(20) + 5; // 范围: 5-24
            } else { // 第一个运算符是乘法
                operands[1] = random.nextInt(8) + 2; // 范围: 2-9
            }

            // 为确保第二个操作数能被整除，我们需要先确定一个能够被整除的数
            int dividend = random.nextInt(40) + 10; // 被除数范围: 10-49

            // 找到能整除这个数的除数
            List<Integer> divisors = findDivisors(dividend);
            if (divisors.isEmpty()) {
                return false;
            }

            // 选择一个除数作为第三个操作数
            operands[2] = divisors.get(random.nextInt(divisors.size()));

            // 调整第二个操作数，确保它是被除数
            operands[1] = dividend;
        }

        return true;
    }

    /**
     * 生成不包含除法的表达式的操作数
     */
    private void generateNormalOperands(int[] operators, int[] operands) {
        // 第一个操作数
        operands[0] = random.nextInt(40) + 10; // 范围: 10-49

        // 根据运算符类型生成其他操作数
        for (int i = 0; i < 2; i++) {
            switch (operators[i]) {
                case PLUS: // 加法
                case MINUS: // 减法
                    operands[i + 1] = random.nextInt(20) + 5; // 范围: 5-24
                    break;
                case MULTIPLY: // 乘法
                    operands[i + 1] = random.nextInt(8) + 2; // 范围: 2-9
                    break;
            }
        }
    }

    /**
     * 计算表达式结果，遵循先乘除后加减的规则
     */
    private int calculateResult(int[] operators, int[] operands) {
        // 复制操作数和运算符，以免修改原始数据
        int[] ops = new int[]{operators[0], operators[1]};
        int[] nums = new int[]{operands[0], operands[1], operands[2]};

        // 先处理乘除运算
        for (int i = 0; i < 2; i++) {
            if (ops[i] == MULTIPLY || ops[i] == DIVIDE) {
                // 执行乘除运算
                nums[i] = performOperation(nums[i], nums[i + 1], ops[i]);

                // 移动后续的运算符和操作数
                if (i < 1) {
                    nums[i + 1] = nums[i + 2];
                    ops[i] = ops[i + 1];
                }

                // 标记处理过的运算符位置
                ops[1] = -1;
                break;
            }
        }

        // 处理加减运算
        if (ops[1] != -1) {
            // 如果有两个加减运算，从左到右执行
            return performOperation(
                    performOperation(nums[0], nums[1], ops[0]),
                    nums[2], ops[1]);
        } else {
            // 如果只剩一个运算符（之前已经处理过一个乘除运算）
            return performOperation(nums[0], nums[1], ops[0]);
        }
    }

    /**
     * 执行单个运算
     */
    private int performOperation(int a, int b, int operatorType) {
        switch (operatorType) {
            case PLUS:
                return a + b;
            case MINUS:
                return a - b;
            case MULTIPLY:
                return a * b;
            case DIVIDE:
                return a / b;
            default:
                return 0;
        }
    }

    /**
     * 查找数字的因数（不包括1和自身）
     */
    private List<Integer> findFactors(int number) {
        List<Integer> factors = new ArrayList<>();

        // 查找2到9之间的因数
        for (int i = 2; i <= 9; i++) {
            if (number % i == 0) {
                factors.add(i);
            }
        }

        return factors;
    }

    /**
     * 查找数字的除数（范围2-9）
     */
    private List<Integer> findDivisors(int number) {
        List<Integer> divisors = new ArrayList<>();

        // 查找2到9之间的除数
        for (int i = 2; i <= 9; i++) {
            if (number % i == 0) {
                divisors.add(i);
            }
        }

        return divisors;
    }

    /**
     * 生成简单的加法表达式作为后备方案
     */
    private Result generateSimplePlus() {
        int result = random.nextInt(maxResult - minResult + 1) + minResult;
        int operand1 = random.nextInt(result - 10) + 10;
        int operand2 = result - operand1;

        String expression = operand1 + " + " + operand2;
        return new Result(expression, result);
    }

    /**
     * 表达式结果类
     */
    public static class Result {
        private final String expression;
        private final int result;

        public Result(String expression, int result) {
            this.expression = expression;
            this.result = result;
        }

        public String getExpression() {
            return expression;
        }

        public int getResult() {
            return result;
        }

        public String getResultAsString() {
            return String.valueOf(result);
        }
    }
}